﻿using Quartz;
using Quartz.Impl;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace GrayParrot.Scheduling
{
    public class Scheduler : IDisposable
    {
        private bool disposed = false; // to detect redundant calls
        private IScheduler scheduler = null;
        TaskTriggerListenerBase myListener = null;

        public Scheduler()
        {
        }

        /// <summary>
        /// Initializes the scheduler instance...
        /// </summary>
        public void Initialize()
        {
            scheduler = StdSchedulerFactory.GetDefaultScheduler();
            scheduler.Start();
        }

        /// <summary>
        /// Starts the scheduler instance--.
        /// </summary>
        public void Start(TaskTriggerListenerBase listener)
        {
            myListener = listener;
            scheduler.ListenerManager.AddTriggerListener(myListener, myListener.KeyMatcher);
        }

        /// <summary>
        /// Stops the scheduler instance...
        /// </summary>
        public void Stop()
        {
            if (myListener != null)
            {
                scheduler.ListenerManager.RemoveTriggerListener(myListener.Name);
                myListener = null;
            }
        }

        public void Pause()
        {
            if (scheduler != null)
            {
                scheduler.PauseTrigger(myListener.TriggerKey);
            }
        }

        public void Resume()
        {
            if (scheduler != null)
            {
                scheduler.ResumeTrigger(myListener.TriggerKey);
            }
        }

        /// <summary>
        /// Sets the schedule for non repeteable task...
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="scheduleDate">The schedule date.</param>
        public void SetSchedule<T>(DateTime scheduleDate, JobParameters parameters = null) where T : IJob
        {
            if (scheduler != null)
            {
                //myListener = new RunOnceTriggerListener();

                //scheduler.ListenerManager.AddTriggerListener(myListener, myListener.KeyMatcher);

                JobDataMap m_map = ((parameters != null) ? parameters.GetParameters() : null);

                //IJobDetail jobInternal = JobBuilder.Create<T>()
                //                                   .WithIdentity(myListener.TriggerKey.Name, myListener.TriggerKey.Group)
                //                                   .SetJobData(m_map)
                //                                   .Build();

                JobBuilder pp = JobBuilder.Create<T>();
                pp.WithIdentity(myListener.TriggerKey.Name, myListener.TriggerKey.Group);
                if (m_map != null)
                {
                    pp.SetJobData(m_map);
                }
                IJobDetail jobInternal = pp.Build();

                ITrigger trigger = TriggerBuilder.Create()
                    .WithIdentity(myListener.TriggerKey.Name, myListener.TriggerKey.Group)
                    .ForJob(jobInternal.Key.Name, jobInternal.Key.Group)
                    .StartAt(new DateTimeOffset(scheduleDate)).Build();

                scheduler.ScheduleJob(jobInternal, trigger);
            }
        }

        /// <summary>
        /// Sets the schedule for repeateable task...
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="listener">The listener.</param>
        /// <param name="cronBuilder">The cron builder.</param>
        public void SetSchedule<T>(TaskTriggerListenerBase listener, CronScheduleBuilder cronBuilder, JobParameters parameters = null) where T : IJob
        {
            if (scheduler != null)
            {
                JobDataMap m_map = ((parameters != null) ? parameters.GetParameters() : null);

                //IJobDetail jobInternal = JobBuilder.Create<T>()
                //                                   .WithIdentity(listener.Name, listener.TriggerKey.Group)
                //                                   .SetJobData(m_map)
                //                                   .Build();

                JobBuilder pp = JobBuilder.Create<T>();
                pp.WithIdentity(listener.Name, listener.TriggerKey.Group);
                if (m_map != null)
                {
                    pp.SetJobData(m_map);
                }
                IJobDetail jobInternal = pp.Build();


                ITrigger trigger = TriggerBuilder.Create()
                    .WithIdentity(listener.TriggerKey)
                    .ForJob(listener.Name, listener.TriggerKey.Group)
                    .WithSchedule(cronBuilder)
                    .Build();

                scheduler.ScheduleJob(jobInternal, trigger);
            }
        }

        /// <summary>
        /// Sets the schedule for repeateable task...
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="cronBuilder">The cron builder.</param>
        public void SetSchedule<T>(CronScheduleBuilder cronBuilder, JobParameters parameters = null) where T : IJob
        {
            if (scheduler != null)
            {
                JobDataMap m_map = parameters.GetParameters();

                //IJobDetail jobInternal = JobBuilder.Create<T>()
                //                                   .SetJobData(m_map)
                //                                   .Build();

                JobBuilder pp = JobBuilder.Create<T>();
                if (m_map != null)
                {
                    pp.SetJobData(m_map);
                }
                IJobDetail jobInternal = pp.Build();

                ITrigger trigger = TriggerBuilder.Create()
                    .WithIdentity(jobInternal.Key.Name, jobInternal.Key.Group)
                    .ForJob(jobInternal.Key.Name, jobInternal.Key.Group)
                    .WithSchedule(cronBuilder)
                    .Build();

                scheduler.ScheduleJob(jobInternal, trigger);
            }
        }

        protected virtual void Dispose(bool disposing)
        {
            if (!disposed)
            {
                if (disposing)
                {
                    // dispose managed code...
                }

                // dispose unmanaged code...
                if (scheduler != null)
                {
                    Stop();
                    //if (myListener != null)
                    //{
                    //    scheduler.ListenerManager.RemoveTriggerListener(myListener.Name);
                    //    myListener = null;
                    //}

                    scheduler.Shutdown(true);
                    scheduler = null;
                }

                //
                disposed = true;
            }
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        //public void SetParameter<T>(T parameter) where T : class
        //{
        //    if (scheduler != null)
        //    {
        //        scheduler.Context.Put(typeof(T).Name, parameter);
        //    }
        //}

        //public void SetParameter(string key, string parameter)
        //{
        //    if (scheduler != null)
        //    {
        //        scheduler.Context.Put(key, parameter);
        //    }
        //}
    }
}
